package org.market.infrastructure.persistent.repository;

import lombok.extern.slf4j.Slf4j;
import org.market.domain.strategy.model.entity.StrategyAwardEntity;
import org.market.domain.strategy.model.entity.StrategyEntity;
import org.market.domain.strategy.model.entity.StrategyRuleEntity;
import org.market.domain.strategy.model.valobj.*;
import org.market.domain.strategy.repository.IStrategyRepository;
import org.market.infrastructure.persistent.dao.*;
import org.market.infrastructure.persistent.po.*;
import org.market.infrastructure.persistent.redis.IRedisService;
import org.market.types.common.Constants;
import org.redisson.api.RBlockingQueue;
import org.redisson.api.RDelayedQueue;
import org.redisson.api.RMap;
import org.springframework.stereotype.Repository;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import static org.market.types.common.Constants.RedisKey.RULE_TREE_VO_KEY;

/**
 * @Author: Insight
 * @Description: 策略仓储实现
 * @Date: 2025/2/4 下午11:02
 * @Version: 1.0
 */
@Slf4j
@Repository
public class StrategyRepository implements IStrategyRepository {
    @Resource
    private IStrategyAwardDao strategyAwardDao;
    @Resource
    private IStrategyDao strategyDao;
    @Resource
    private IStrategyRuleDao strategyRuleDao;

    @Resource
    private IRedisService redisService;

    @Resource
    private IRuleTreeDao ruleTreeDao;
    @Resource
    private IRuleTreeNodeDao ruleTreeNodeDao;
    @Resource
    private IRuleTreeNodePathDao ruleTreeNodePathDao;
    @Override
    public List<StrategyAwardEntity> queryStrategyAwardList(Long strategyId) {
        /** 1.先看一下redis是否有缓存*/
        String cacheKey = Constants.RedisKey.STRATEGY_AWARD_KEY + strategyId;
        List<StrategyAwardEntity> awardEntities = redisService.getValue(cacheKey);
        if (null != awardEntities && !awardEntities.isEmpty()) {
            return awardEntities;
        }

        /**2.redis查不到在进数据库中查找*/
        List<StrategyAward> strategyAwardList  = strategyAwardDao.queryStrategyAwardListByStrategyId(strategyId);
        awardEntities = new ArrayList<>(strategyAwardList.size());
        for (StrategyAward strategyAward : strategyAwardList) {
            StrategyAwardEntity strategyAwardEntity = StrategyAwardEntity.builder()
                        .strategyId(strategyAward.getStrategyId())
                        .awardId(strategyAward.getAwardId())
                        .awardCount(strategyAward.getAwardCount())
                        .awardCountSurplus(strategyAward.getAwardCountSurplus())
                        .awardRate(strategyAward.getAwardRate())
                        .build();
            awardEntities.add(strategyAwardEntity);
        }

        /**3.将缓存写进redis中*/
        redisService.setValue(cacheKey,awardEntities);
        return awardEntities;
    }

    @Override
    public void storeStrategyAwardSearchRateTable(String key, BigDecimal rateRange, HashMap<Integer, Integer> shuffleStrategyAwardSearchRateTable) {
        //存储概率抽奖范围,例如1000,让他生成1,1000的随机数
        redisService.setValue(Constants.RedisKey.STRATEGY_RATE_RANGE_KEY + key, rateRange.intValue());
        //存储概率查找表,先取出,再存入
        RMap<Integer, Integer> map = redisService.getMap(Constants.RedisKey.STRATEGY_RATE_TABLE_KEY + key);
        map.putAll(shuffleStrategyAwardSearchRateTable);
    }

    @Override
    public int getRateRange(Long strategyId) {
        return getRateRange(String.valueOf(strategyId));
    }

    @Override
    public int getRateRange(String key) {
        //直接从redis中获取已经存放的--概率范围--
        return redisService.getValue(Constants.RedisKey.STRATEGY_RATE_RANGE_KEY + key);
    }


    @Override
    public Integer getStrategyAwardAssemble(String key, Integer rateKey) {
        //拿着随机生成的随机数,在map中找
        return redisService.getFromMap(Constants.RedisKey.STRATEGY_RATE_TABLE_KEY + key, rateKey);
    }

    @Override
    public StrategyEntity queryStrategyEntityList(Long strategyId) {
        //先从redis中找
        String key = Constants.RedisKey.STRATEGY_KEY + strategyId;
        StrategyEntity strategyCache = redisService.getValue(key);
        if (strategyCache != null) return strategyCache;
        //在数据库中找strategyPO再装配到实体类中
        Strategy strategy = strategyDao.queryStrategyByStrategyId(strategyId);
        StrategyEntity strategyEntity = StrategyEntity.builder()
                .strategyId(strategyId)
                .strategyDesc(strategy.getStrategyDesc())
                .ruleModels(strategy.getRuleModels())
                .build();
        //缓存到redis中去
        redisService.setValue(key,strategyEntity);
        return strategyEntity;
    }

    @Override
    public StrategyRuleEntity queryStrategyRuleEntity(Long strategyId, String ruleModel) {
        String key = Constants.RedisKey.STRATEGY_RULE_WEIGHT_KEY + strategyId;
        StrategyRuleEntity strategyRuleCache = redisService.getValue(key);
        if (strategyRuleCache != null) return strategyRuleCache;
        //在数据库中找strategyPO再装配到实体类中
        StrategyRule strategyRule = strategyRuleDao.queryStrategyRuleList(strategyId,ruleModel);
        StrategyRuleEntity strategyRuleEntity = StrategyRuleEntity.builder()
                .strategyId(strategyId)
                .awardId(strategyRule.getAwardId())
                .ruleType(strategyRule.getRuleType())
                .ruleModel(strategyRule.getRuleModel())
                .ruleValue(strategyRule.getRuleValue())
                .ruleDesc(strategyRule.getRuleDesc())
                .build();

        //缓存到redis中去
        //TODO
        redisService.setValue(key,strategyRuleEntity);
        return strategyRuleEntity;
    }

    /**
     * 查询抽奖权重规则
     * @param strategyId
     * @param awardId
     * @param ruleModel
     * @return
     */
    @Override
    public String queryStrategyRuleValue(Long strategyId, Integer awardId, String ruleModel) {
        StrategyRule strategyRule = new StrategyRule();
        strategyRule.setStrategyId(strategyId);
        strategyRule.setAwardId(awardId);
        strategyRule.setRuleModel(ruleModel);
        return strategyRuleDao.queryStrategyRuleValue(strategyRule);
    }

    @Override
    public String queryStrategyRuleValue(Long strategyId, String ruleModel) {
        return queryStrategyRuleValue(strategyId,null,ruleModel);
    }

    /**
     * 根据策略id查询策略实体
     * @param strategyId
     * @return
     */
    @Override
    public StrategyEntity queryStrategyEntityByStrategyId(Long strategyId) {
        return queryStrategyEntityList(strategyId);
    }

    /**
     * 根据用户id和活动策略id查询:规则模型实体
     * 例如:rule_lock,rule_luck
     * @param strategyId
     * @param awardId
     * @return
     */
    @Override
    public StrategyAwadRuleModelVO queryStrategyAwardRuleModel(Long strategyId, Integer awardId) {
        StrategyAward strategyAward = new StrategyAward();
        strategyAward.setStrategyId(strategyId);
        strategyAward.setAwardId(awardId);
        String ruleModels = strategyAwardDao.queryStrategyAwardRuleModel(strategyAward);
        return StrategyAwadRuleModelVO.builder()
                .ruleModels(ruleModels)
                .build();
    }

    @Override
    public RuleTreeVO queryRuleTreeVOByTreeId(String treeId) {
        String redisKey = RULE_TREE_VO_KEY + treeId;
        RuleTreeVO cache = redisService.getValue(redisKey);
        if (cache != null) return cache;

        //获取RuleTree
        RuleTree ruleTrees = ruleTreeDao.queryRuleTreeByTreeId(treeId);
        //获取RuleTreeNodeVO
        List<RuleTreeNode> ruleTreeNodeList = ruleTreeNodeDao.queryRuleTreeNodeByTreeId(treeId);
        //获取RuleTreeNodePathVO
        List<RuleTreeNodePath> ruleTreeNodePathList = ruleTreeNodePathDao.queryRuleTreeNodeByTreeId(treeId);


        //用map将
        Map<String,List<RuleTreeNodePathVO>> ruleTreeNodePathMap = new HashMap<>();
        for (RuleTreeNodePath ruleTreeNodePath : ruleTreeNodePathList) {
            RuleTreeNodePathVO ruleTreeNodePathVO = RuleTreeNodePathVO.builder()
                    .treeId(treeId)
                    .from(ruleTreeNodePath.getFrom())
                    .to(ruleTreeNodePath.getTo())
                    .ruleLimitType(RuleTreeLimitVO.valueOf(ruleTreeNodePath.getTreeLimitType()))
                    .logicLimitValue(RuleLogicCheckTypeVO.valueOf(ruleTreeNodePath.getTreeLimitValue()))
                    .build();
            List<RuleTreeNodePathVO> nodePaths = ruleTreeNodePathMap.computeIfAbsent(ruleTreeNodePathVO.getFrom(), k -> new ArrayList<>());
            nodePaths.add(ruleTreeNodePathVO);
        }

        Map<String,RuleTreeNodeVO> ruleTreeNodeVOMap = new HashMap<>();
        for (RuleTreeNode ruleTreeNode : ruleTreeNodeList) {
            RuleTreeNodeVO ruleTreeNodeVO = RuleTreeNodeVO.builder()
                    .treeId(treeId)
                    .key(ruleTreeNode.getKey())
                    .value(ruleTreeNode.getValue())
                    .desc(ruleTreeNode.getRuleDesc())
                    .pathList(ruleTreeNodePathMap.get(ruleTreeNode.getKey()))
                    .build();
            ruleTreeNodeVOMap.put(ruleTreeNode.getKey(),ruleTreeNodeVO);
        }

        RuleTreeVO ruleTreeVO = RuleTreeVO.builder()
                .treeName(ruleTrees.getTreeName())
                .treeId(treeId)
                .desc(ruleTrees.getRuleDesc())
                .treeRootNode(ruleTrees.getTreeNode())
                .nodeGroup(ruleTreeNodeVOMap)
                .build();

        //TODO
        redisService.setValue(redisKey,ruleTreeVO);
        return ruleTreeVO;
    }

    @Override
    public void cacheStrategyAwardCount(String cacheKey, Long awardCount) {
        Long value = redisService.getAtomicLong(cacheKey);
        if (value == null){
            log.info("奖品库存已经存在过,返回信息:{}",awardCount);
            return;
        }
        redisService.setAtomicLong(cacheKey,awardCount);
    }

    @Override
    public Boolean querySubtractAwardCount(String cacheKey) {
        long surplus = redisService.decr(cacheKey);
        if (surplus < 0){
            redisService.setValue(cacheKey,0);
            return false;
        }
        String key = cacheKey+Constants.UNDERLINE+surplus;
        Boolean lock = redisService.setNx(key);
        if (!lock){
            log.info("奖品库存加锁失败,返回信息:{}",key);
            return false;
        }
        return lock;
    }

    @Override
    public void awardStockConsumeSendQueue(StrategyAwardStockKeyVO build) {
        String cacheKey = Constants.RedisKey.STRATEGY_AWARD_COUNT_QUERY_KEY;
        RBlockingQueue<StrategyAwardStockKeyVO> blockingQueue = redisService.getBlockingQueue(cacheKey);
        RDelayedQueue<StrategyAwardStockKeyVO> delayedQueue = redisService.getDelayedQueue(blockingQueue);
        delayedQueue.offer(build,3, TimeUnit.SECONDS);
    }

    @Override
    public StrategyAwardStockKeyVO queryTakeQueueValue() {
        String cacheKey = Constants.RedisKey.STRATEGY_AWARD_COUNT_QUERY_KEY;
        RBlockingQueue<StrategyAwardStockKeyVO> blockingQueue = redisService.getBlockingQueue(cacheKey);
        return blockingQueue.poll();
    }

    @Override
    public void updateStrategyAwardStock(Long strategyId, Integer awardId) {
        StrategyAward strategyAward = new StrategyAward();
        strategyAward.setStrategyId(strategyId);
        strategyAward.setAwardId(awardId);
        strategyAwardDao.updateStrategyAwardStock(strategyAward);
    }

}
